package com.tbit.uqbike.client.remote;

import com.alibaba.fastjson.JSON;
import com.tbit.common.utils.SpringContextUtils;
import com.tbit.uqbike.client.dao.core.AccountDao;
import com.tbit.uqbike.client.pojo.Account;
import com.tbit.uqbike.client.pojo.WeixinPayConfig;
import com.tbit.uqbike.client.pojo.WeixinPayParam;
import com.tbit.uqbike.client.pojo.WxPayDto;
import com.tbit.uqbike.client.util.*;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Amount;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Payer;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.Status;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 微信支付
 *
 * @author Leon
 * 2015-11-10 下午04:57:38
 */
@Slf4j
@RefreshScope
@Configuration
public class WeixinPay {
    /** 服务商 商户号 */
    public static String sp_merchantId ;
    /** 服务商 商户API私钥路径 */
    public static String sp_privateKeyPath ;
    /** 服务商 商户证书序列号 */
    public static String sp_merchantSerialNumber ;
    /** 服务商 商户APIV3密钥 */
    public static String sp_apiV3Key ;
    /** 服务商 appid */
    public static  String sp_appid ;
    /** 服务商配置类 */
    public static Config config;

    @Value("${wx_mch_appid:1664343558}")
    public void setSp_merchantId(String sp_merchantId) {
        WeixinPay.sp_merchantId = sp_merchantId;
    }
    @Value("${wx_mch_privateKeyPath:/data/cert/mch/apiclient_key.pem}")
    public void setSp_privateKeyPath(String sp_privateKeyPath) {
        WeixinPay.sp_privateKeyPath = sp_privateKeyPath;
    }
    @Value("${wx_mch_merchantSerialNumber:59E67ECF1ACA6ACEDF74A4CC245E19C63598FE79}")
    public void setSp_merchantSerialNumber(String sp_merchantSerialNumber) {
        WeixinPay.sp_merchantSerialNumber = sp_merchantSerialNumber;
    }
    @Value("${wx_mch_apiV3Key:tbit74123client36987TBITservices}")
    public void setSp_apiV3Key(String sp_apiV3Key) {
        WeixinPay.sp_apiV3Key = sp_apiV3Key;
    }
    @Value("${sp_appid:wx2871656e2021f65f}")
    public void setSp_appid(String sp_appid) {
        WeixinPay.sp_appid = sp_appid;
    }

    // todo 是否需要支持动态配置
    @PostConstruct
    public void init(){
        log.debug("调用init方法，参数" + sp_merchantId + "  " + sp_privateKeyPath + "  " + sp_merchantSerialNumber + "  " + sp_apiV3Key + "  " + sp_appid);
        config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(sp_merchantId)
                        .privateKeyFromPath(sp_privateKeyPath)
                        .merchantSerialNumber(sp_merchantSerialNumber)
                        .apiV3Key(sp_apiV3Key)
                        .build();
    }

    /**
     * 统一入口
     * 支付调用
     * 获取请求预支付id报文
     *
     * @return
     */
    @SuppressWarnings("static-access")
    public static String getPackage(WeixinPayConfig weixinPayConfig, WxPayDto tpWxPayDto, Boolean isMchPay) {
        log.info("是否服务商支付{}，使用的微信支付配置为{}",isMchPay, JSON.toJSONString(weixinPayConfig));
        if (isMchPay){
            return mchPay(weixinPayConfig, tpWxPayDto);
        }else {
            return normalPay(weixinPayConfig, tpWxPayDto);
        }
    }

    /**
     * 普通商户支付 获取请求预支付id报文 V2
     * @param weixinPayConfig
     * @param tpWxPayDto
     * @return
     */
    @NotNull
    private static String normalPay(WeixinPayConfig weixinPayConfig, WxPayDto tpWxPayDto) {
        String openId = tpWxPayDto.getOpenId();
        // 1 参数
        // 订单号
        String orderId = tpWxPayDto.getOrderId();
        // 总金额以分为单位，不带小数点
        String totalFee = WeixinPayUtil.getMoney(tpWxPayDto.getTotalFee());

        // 订单生成的机器 IP
        String spbill_create_ip = tpWxPayDto.getSpbillCreateIp();
        // 这里notify_url是 支付完成后微信发给该链接信息，可以判断会员是否支付成功，改变订单状态等。
        String notify_url = weixinPayConfig.getNotifyUrl();
        String trade_type = "JSAPI";

        // ---必须参数
        // 商户号
        String mch_id = weixinPayConfig.getPartner();
        // 随机字符串
        String nonce_str = WeixinPayUtil.getNonceStr(false);

        // 商品描述根据情况修改
        String body = tpWxPayDto.getBody();
        // 附加数据 原样返回
        String attach = body == null ? "" : body;
        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid", weixinPayConfig.getAppId());
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", body);
        packageParams.put("attach", attach);
        packageParams.put("out_trade_no", orderId);

        // 这里写的金额为1 分到时修改
        packageParams.put("total_fee", totalFee);
        packageParams.put("spbill_create_ip", spbill_create_ip);
        packageParams.put("notify_url", notify_url);

        packageParams.put("trade_type", trade_type);
        packageParams.put("openid", openId);

        String time_expire = DateTimeUtil.getNowTimeyyyyMMddHHmmss();
        /**添加交易截止时间*/
        packageParams.put("time_expire", time_expire);

        RequestHandler reqHandler = new RequestHandler(null, null);
        reqHandler.init(weixinPayConfig.getAppId(), weixinPayConfig.getSecret(), weixinPayConfig.getPartnerkey());

        String sign = reqHandler.createSign(packageParams);
        String xml = "<xml>"
                + "<appid>" + weixinPayConfig.getAppId() + "</appid>"
                + "<mch_id>" + mch_id + "</mch_id>"
                + "<nonce_str>" + nonce_str + "</nonce_str>"
                + "<sign>" + sign + "</sign>"
                + "<body><![CDATA[" + body + "]]></body>"
                + "<out_trade_no>" + orderId + "</out_trade_no>"
                + "<attach>" + attach + "</attach>"
                + "<total_fee>" + totalFee + "</total_fee>"
                + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
                + "<notify_url>" + notify_url + "</notify_url>"
                + "<trade_type>" + trade_type + "</trade_type>"
                + "<openid>" + openId + "</openid>"
                + "<time_expire>" + time_expire + "</time_expire>"
                + "</xml>";
        String prepay_id = "";
        String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        AccountDao accountDao = SpringContextUtils.getBean(AccountDao.class);
        log.info("通过SpringContextUtils获取到AccountDao={}",accountDao);
        Account account = accountDao.getByAccountId(weixinPayConfig.getAccountId());
        prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml, account.getName());

        //获取prepay_id后，拼接最后请求支付所需要的package
        SortedMap<String, String> finalpackage = new TreeMap<String, String>();
        String timestamp = Sha1Util.getTimeStamp();
        String packages = "prepay_id=" + prepay_id;
        finalpackage.put("appId", weixinPayConfig.getAppId());
        finalpackage.put("timeStamp", timestamp);
        finalpackage.put("nonceStr", nonce_str);
        finalpackage.put("package", packages);
        finalpackage.put("signType", "MD5");
        //要签名
        String finalsign = reqHandler.createSign(finalpackage);

        String finaPackage = "'appId':'" + weixinPayConfig.getAppId() + "','timeStamp':'" + timestamp
                + "','nonceStr':'" + nonce_str + "','package':'"
                + packages + "','signType' : 'MD5" + "','paySign':'"
                + finalsign + "'";
        log.info("微信预支付最后的到的结果{}",finaPackage);
        return finaPackage;
    }

    /**
     * 服务商支付 获取请求预支付id报文 V3
     * @param weixinPayConfig
     * @param tpWxPayDto
     * @return
     */
    @NotNull
    private static String mchPay(WeixinPayConfig weixinPayConfig, WxPayDto tpWxPayDto) {
        String openId = tpWxPayDto.getOpenId();
        // 1 参数
        // 订单号
        String orderId = tpWxPayDto.getOrderId();
        // 总金额以分为单位，不带小数点
        Integer totalFee = Integer.parseInt(WeixinPayUtil.getMoney(tpWxPayDto.getTotalFee()));
        // 回调地址
        String notify_url = weixinPayConfig.getNotifyUrl();
        // 商户号
        String mch_id = weixinPayConfig.getPartner();
        // 商品描述根据情况修改
        String body = tpWxPayDto.getBody();
        // 商户订单号
        String app_id = weixinPayConfig.getAppId();

        // 构建service
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(totalFee);
        request.setAmount(amount);
        request.setSpAppid(sp_appid);
        request.setSpMchid(sp_merchantId);
        request.setSubAppid(app_id);
        request.setSubMchid(mch_id);
        request.setDescription(body);
        request.setNotifyUrl(notify_url);
        request.setOutTradeNo(orderId);
        String time_expire = DateTimeUtil.timeStampToRfc3339(System.currentTimeMillis() + 61 * 1000L);
        request.setTimeExpire(time_expire);

        Payer payer = new Payer();
        // 支付用户的openid
        payer.setSubOpenid(openId);
        request.setPayer(payer);
        // 调用下单方法，得到应答
        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request,app_id);
        // 使用微信扫描 code_url 对应的二维码，即可体验Native支付
        String res = "'appId':'" + response.getAppId() + "','timeStamp':'" + response.getTimeStamp() +
                "','nonceStr':'" + response.getNonceStr() + "','package':'" + response.getPackageVal() +
                "','signType' : '" + response.getSignType() + "','paySign':'" + response.getPaySign() + "'";
        log.debug("服务商支付返回结果"+res);

        return res;
    }


    /**
     * 获取请求预支付id报文
     *
     * @return
     */
    @SuppressWarnings("static-access")
    public static WeixinPayParam getPackage_app(WeixinPayConfig weixinPayConfig, WxPayDto tpWxPayDto) {
        WeixinPayParam wpp = new WeixinPayParam();

        // 1 参数
        // 订单号
        String orderId = tpWxPayDto.getOrderId();
        // 附加数据 原样返回
        String attach = "";
        // 总金额以分为单位，不带小数点
        String totalFee = WeixinPayUtil.getMoney(tpWxPayDto.getTotalFee());

        // 订单生成的机器 IP
        String spbill_create_ip = tpWxPayDto.getSpbillCreateIp();
        // 这里notify_url是 支付完成后微信发给该链接信息，可以判断会员是否支付成功，改变订单状态等。
        String notify_url = weixinPayConfig.getNotifyUrl();
        String trade_type = "APP";

        // ---必须参数
        // 商户号
        String mch_id = weixinPayConfig.getPartner();
        // 随机字符串
        String nonce_str = WeixinPayUtil.getNonceStr(false);

        // 商品描述根据情况修改
        String body = tpWxPayDto.getBody();

        // 商户订单号
        String out_trade_no = orderId;

        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid", weixinPayConfig.getAppId());
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", body);
        packageParams.put("out_trade_no", out_trade_no);
        packageParams.put("attach", attach);
        packageParams.put("total_fee", totalFee);
        packageParams.put("spbill_create_ip", spbill_create_ip);
        packageParams.put("notify_url", notify_url);
        packageParams.put("trade_type", trade_type);

        RequestHandler reqHandler = new RequestHandler(null, null);
        reqHandler.init(weixinPayConfig.getAppId(), weixinPayConfig.getSecret(), weixinPayConfig.getPartnerkey());

        String sign = reqHandler.createSign(packageParams);
        String xml = "<xml>"
                + "<appid>" + weixinPayConfig.getAppId() + "</appid>"
                + "<mch_id>" + mch_id + "</mch_id>"
                + "<nonce_str>" + nonce_str + "</nonce_str>"
                + "<sign>" + sign + "</sign>"
                + "<body><![CDATA[" + body + "]]></body>"
                + "<out_trade_no>" + out_trade_no + "</out_trade_no>"
                + "<attach>" + attach + "</attach>"
                + "<total_fee>" + totalFee + "</total_fee>"
                + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
                + "<notify_url>" + notify_url + "</notify_url>"
                + "<trade_type>" + trade_type + "</trade_type>"
                + "</xml>";
        String prepay_id = "";
        String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        AccountDao accountDao = SpringContextUtils.getBean(AccountDao.class);
        log.info("通过SpringContextUtils获取到AccountDao={}",accountDao);
        Account account = accountDao.getByAccountId(weixinPayConfig.getAccountId());
        /**调试打印*/
//		System.out.println(xml);
        prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml, account.getName());

        //获取prepay_id后，拼接最后请求支付所需要的package
        SortedMap<String, String> finalpackage = new TreeMap<String, String>();
        String timestamp = Sha1Util.getTimeStamp();

        finalpackage.put("appid", weixinPayConfig.getAppId());
        finalpackage.put("partnerid", weixinPayConfig.getPartner());
        finalpackage.put("prepayid", prepay_id);
        finalpackage.put("package", "Sign=WXPay");
        finalpackage.put("noncestr", nonce_str);
        finalpackage.put("timestamp", timestamp);

        //生成签名
        String finalsign = reqHandler.createSign(finalpackage);

        wpp.setOrderNO(out_trade_no);
        wpp.setTotalFee(tpWxPayDto.getTotalFee());
        wpp.setPrepayid(prepay_id);
        wpp.setTimestamp(timestamp);
        wpp.setWxpackage("Sign=WXPay");
        wpp.setNoncestr(nonce_str);
        wpp.setSign(finalsign);

        return wpp;
    }

    /**
     * 押金退还
     *
     * @param weixinPayConfig
     */
    @SuppressWarnings("static-access")
    public static Boolean return_deposit(WeixinPayConfig weixinPayConfig, String transaction_id, String out_trade_no, String out_refund_no, String fee) {
        /**退款接口*/
        String createOrderURL = "https://api.mch.weixin.qq.com/secapi/pay/refund";

        // 随机字符串
        String nonce_str = WeixinPayUtil.getNonceStr(true);

        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid", weixinPayConfig.getAppId());
        packageParams.put("mch_id", weixinPayConfig.getPartner());
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("out_refund_no", out_refund_no);
        packageParams.put("out_trade_no", out_trade_no);
        packageParams.put("refund_fee", fee);
        packageParams.put("total_fee", fee);
        packageParams.put("transaction_id", transaction_id);
        RequestHandler reqHandler = new RequestHandler(null, null);
        reqHandler.init(weixinPayConfig.getAppId(), weixinPayConfig.getSecret(), weixinPayConfig.getPartnerkey());

        String sign = reqHandler.createSign(packageParams);
        String xml = "<xml>"
                + "<appid>" + weixinPayConfig.getAppId() + "</appid>"
                + "<mch_id>" + weixinPayConfig.getPartner() + "</mch_id>"
                + "<nonce_str>" + nonce_str + "</nonce_str>"
                + "<out_refund_no>" + out_refund_no + "</out_refund_no>"
                + "<out_trade_no>" + out_trade_no + "</out_trade_no>"
                + "<refund_fee>" + fee + "</refund_fee>"
                + "<total_fee>" + fee + "</total_fee>"
                + "<transaction_id>" + transaction_id + "</transaction_id>"
                + "<sign>" + sign + "</sign>"
                + "</xml>";
        return new GetWxOrderno().return_deposit(weixinPayConfig.getAccountId().toString(), weixinPayConfig.getType(), createOrderURL, xml, weixinPayConfig.getPartner());
    }


    /**
     * 统一入口
     * 退款调用
     * @param weixinPayConfig
     * @param out_trade_no
     * @param out_refund_no
     * @param refund_fee
     * @param fee
     * @param isMchPay
     * @return
     */
    public static Boolean returnMoney(WeixinPayConfig weixinPayConfig, String out_trade_no, String out_refund_no, String refund_fee, String fee, Boolean isMchPay) {
        log.info("是否服务商退款{}，使用的微信支付配置为{}",isMchPay, JSON.toJSONString(weixinPayConfig));
        if (isMchPay){
            return mchReturnMoney(weixinPayConfig, out_trade_no, out_refund_no, refund_fee, fee);
        }else {
            return normalReturnMoney(weixinPayConfig, out_trade_no, out_refund_no, refund_fee, fee);
        }
    }

    /**
     * 服务商退款 V3
     *
     * @param weixinPayConfig
     * @param outTradeNo
     * @param outRefundNo
     * @param refundFee
     * @param fee
     * @return
     */
    private static Boolean mchReturnMoney(WeixinPayConfig weixinPayConfig, String outTradeNo, String outRefundNo, String refundFee, String fee) {
        RefundService refundService = new RefundService.Builder().config(config).build();
        // 退款结果
        Refund refund = null;
        try {
            CreateRequest request = new CreateRequest();
            request.setOutTradeNo(outTradeNo);
            request.setOutRefundNo(outRefundNo);
            request.setSubMchid(weixinPayConfig.getPartner());

            // 订单金额信息
            AmountReq amountReq = new AmountReq();
            amountReq.setTotal(Long.parseLong(fee));
            amountReq.setRefund(Long.parseLong(refundFee));
            amountReq.setCurrency("CNY");
            request.setAmount(amountReq);

            request.setNotifyUrl(weixinPayConfig.getNotifyUrl());

//            request.setFundsAccount();
//            request.setGoodsDetail();
//            request.setReason();
//            request.setTransactionId();

            refund = refundService.create(request);
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            log.error("发送HTTP请求失败{}",e.getHttpRequest());
            return false;
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            log.error("服务返回状态小于200或大于等于300{}",e.getResponseBody());
            return false;
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
            log.error("返回体类型不合法{}",e.getMessage());
            return false;
        }
        return Objects.nonNull(refund) && (refund.getStatus().equals(Status.SUCCESS) || refund.getStatus().equals(Status.PROCESSING));
    }


    /**
     * 普通商户退款 V2
     *
     * @param weixinPayConfig
     * @param out_trade_no
     * @param out_refund_no
     * @param refund_fee
     * @param fee
     * @return
     */
    private static Boolean normalReturnMoney(WeixinPayConfig weixinPayConfig, String out_trade_no, String out_refund_no, String refund_fee, String fee) {
        /**退款接口*/
        String createOrderURL = "https://api.mch.weixin.qq.com/secapi/pay/refund";

        // 随机字符串
        String nonce_str = WeixinPayUtil.getNonceStr(true);

        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid", weixinPayConfig.getAppId());
        packageParams.put("mch_id", weixinPayConfig.getPartner());
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("out_refund_no", out_refund_no);
        packageParams.put("out_trade_no", out_trade_no);
        packageParams.put("refund_fee", refund_fee);
        packageParams.put("total_fee", fee);
        packageParams.put("notify_url", weixinPayConfig.getNotifyUrl());
        RequestHandler reqHandler = new RequestHandler(null, null);
        reqHandler.init(weixinPayConfig.getAppId(), weixinPayConfig.getSecret(), weixinPayConfig.getPartnerkey());
        String sign = reqHandler.createSign(packageParams);
        String xml = "<xml>"
                + "<appid>" + weixinPayConfig.getAppId() + "</appid>"
                + "<mch_id>" + weixinPayConfig.getPartner() + "</mch_id>"
                + "<nonce_str>" + nonce_str + "</nonce_str>"
                + "<out_refund_no>" + out_refund_no + "</out_refund_no>"
                + "<out_trade_no>" + out_trade_no + "</out_trade_no>"
                + "<refund_fee>" + refund_fee + "</refund_fee>"
                + "<total_fee>" + fee + "</total_fee>"
                + "<notify_url>" + weixinPayConfig.getNotifyUrl() + "</notify_url>"
                + "<sign>" + sign + "</sign>"
                + "</xml>";
        return new GetWxOrderno().return_deposit(weixinPayConfig.getAccountId().toString(), weixinPayConfig.getType(), createOrderURL, xml, weixinPayConfig.getPartner());
    }


}
